Imports

library(AlphaSimR)
library(devtools)
library(dplyr)
library(ggplot2)
library(factoextra)
library(patchwork)
library(plotly)
library(purrr)
library(reshape2)
library(snow)
library(tibble)
library(tidyr)
library(viridis)
rm(list = ls())

set.seed(123)

source("Functions/Fitness.R")
source("Functions/TraitArchitecture.R")
source("Scripts/GlobalVariables.R")
source("Scripts/CreateFounderPop.R")

output_dir <- file.path(getwd(), "Output")
if (!dir.exists(output_dir)) dir.create(output_dir)

View a fitness landscape

p <- plotFitnessLandscape()

save_dir <- file.path(output_dir, "FitnessFunctions")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "fitness_function.html")
htmlwidgets::saveWidget(as_widget(p), fname)

Plot different trait architectures according to different algorithms

save_dir <- file.path(output_dir, "TraitArchitecture")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

# Additive effects
methodType <- "Additive"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
                path=save_dir,
                device = "pdf")

# Effect of each allele on fitness
methodType <- "Fitness"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
                path=save_dir,
                device = "pdf")

p <- plot3dPopulationFitness(founderPop, calculateFitnessTwoTrait)

fname <- file.path(save_dir, "3dpopulationfitness.html")
htmlwidgets::saveWidget(as_widget(p), fname)

Simulate several adaptive walks with different population sizes

# Different starting population sizes
n.popSizes <- c(40,200,1000)

# Don't use a SNP chip for these simulations
addSnpChip <- FALSE

fig <- plot_ly()
fit_df <- data.frame(gen=1:n.gens,
                   fitness=numeric(n.gens),
                   traitValA=numeric(n.gens),
                   traitValB=numeric(n.gens))

# Iterate through each population size
for (p in c(1:length(n.popSizes))) {
  n.popSize <- n.popSizes[p]
  print(n.popSize)
  # Create a new population of that size
  source("Scripts/CreateFounderPop.R")
  pop <- founderPop
  # Iterate through the generations, update the result dataframe, and advance
  # progeny based on the two trait fitness funcion
  for(gen in 1:n.gens) {
    fit_df$fitness[gen] <- mean(twoTraitFitFunc(gv(pop)))
    fit_df$traitValA[gen] <- meanG(pop)[1]
    fit_df$traitValB[gen] <- meanG(pop)[2]
    pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*(n.selProp), nCrosses=n.popSize)
  }
  # Add a trace to represent this population's adaptive walk
  fig <- add_trace(
    fig,
    fit_df,
    name = n.popSize,
    x = fit_df$traitValA,
    y = fit_df$traitValB,
    z = fit_df$fitness,
    type = 'scatter3d',
    mode = 'lines',
    opacity = 1,
    color = p,
    line = list(width = 5)
  )
  
  
}
[1] 40
[1] 200
[1] 1000
fig <- fig %>%
  layout(legend=list(title=list(text='Population Size')),
         scene = list(xaxis = list(title = "Trait A"),
                      yaxis = list(title = "Trait B"),
                      zaxis = list(title = "Fitness"),
                      aspectmode='cube')) %>% hide_colorbar()


save_dir <- file.path(output_dir, "DifferentPopulationSizes")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

fname <- file.path(save_dir, paste0("populationSizes_", n.popSizes[1], ",", n.popSizes[2], ",", n.popSizes[3], ".html"))
htmlwidgets::saveWidget(as_widget(fig), fname)  

# Reset variables
source("Scripts/CreateFounderPop.R")
source("Scripts/GlobalVariables.R")

Overlay an adaptive walk over a fitness landscape, and save 3D and 2D versions

fig <- plot_ly()
fit_df <- data.frame(gen=1:n.gens,
                   fitness=numeric(n.gens),
                   traitValA=numeric(n.gens),
                   traitValB=numeric(n.gens))

pop <- founderPop

for(gen in 1:n.gens) {
  fit_df$fitness[gen] <- mean(twoTraitFitFunc(gv(pop)))
  fit_df$traitValA[gen] <- meanG(pop)[1]
  fit_df$traitValB[gen] <- meanG(pop)[2]
  meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
  selRat <- selectionRatio(meanFitness)
  pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*selRat, nCrosses=n.popSize)
}

save_dir <- file.path(output_dir, "OverlayAdaptiveWalk")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)

plotType <- "CONTOUR"
pc <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(pc), fname)

plotType <- "SURFACE"
ps <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(ps), fname)

write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

Plot the change in allele frequency over time

save_dir <- file.path(output_dir, "AlleleFrequencies")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

ggplot2::ggsave(filename = paste0("allelefrequencies.pdf"),
                path=save_dir,
                device = "pdf")
Saving 7 x 7 in image

Simulate an adaptive walk, and plot the following curve: #segregating QTL/#segregating loci

Original question: out of segregating alleles left in the population, how many of them would increase fitness? Is this even a valid question?

TODO: - How does this change with size of allele? - How does size of ‘fitness delta’ change over generations? - Simualate mutations for a single individual to figure out P(allelic substitution is favorable), should reflect FKO - P(mutation gets fixed) - show that this matches Kimura - Compare adaptive walks for DH/inbred population where there are new mutations VS population w/standing genetic variation

n.gens <- 200

# Reset variables
source("Scripts/CreateFounderPop.R")
source("Scripts/GlobalVariables.R")

pop <- founderPop
df <- data.frame(gen=c(),
                 fitness=c(),
                 traitVal1=c(),
                 traitVal2=c(),
                 percFitInc=c(),
                 segAlleles=c(),
                 segQtl=c())

# Iterate through the generations
for (gen in 1:n.gens) {
  print(gen)
  # Counter variables
  numHetLoci = 0
  numHetQtl = 0
  numInc = 0

  # Get all of the loci from the population
  segSiteGeno <- pullSegSiteGeno(pop)
  # Get all of the QTL from the population (a sampling of the segSiteGeno)
  qtlGeno <- getUniqueQtl(pop)
  qtlLoci <- colnames(qtlGeno)
  loci <- colnames(segSiteGeno)
  nLoci <- ncol(segSiteGeno)
  # Iterate through all of the loci
  # TODO: just calculate numHetQtl / numHetLoci
  for (l in 1:ncol(segSiteGeno)) {
    id <- loci[l]
    locus = segSiteGeno[,l]
    # Check if the locus is segregating
    if (hetLocus(locus)) {
      # Increment the counter
      numHetLoci <- numHetLoci + 1
      # Determine the effect size - this is where the bug is
      #e <- getEffectSize(locus, id, pop, "Fitness")
      #if (e > 0) {
      #  numInc <- numInc +1
      #}
      # If the locus is a QTL, then it has an effect size
      if (id %in% qtlLoci) {
        numHetQtl <- numHetQtl + 1
      }
    }
  }

  # Calculate the ratio of segregating QTL to segregating alleles
  if (numHetLoci == 0) {
    perc <- 0
  } else {
    perc <- (numHetQtl / numHetLoci)
  }
  df <- rbind(df, data.frame(gen=gen,
                             fitness=mean(twoTraitFitFunc(gv(pop))),
                             traitVal1=meanG(pop)[1],
                             traitVal2=meanG(pop)[2],
                             percFitInc=perc,
                             segAlleles=numHetLoci,
                             segQtl=numHetQtl))
  # If the population is within n.margin, terminate the simulation
  if (mean(twoTraitFitFunc(gv(pop))) >= n.margin) {
    break
  }
  meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
  selRat <- selectionRatio(meanFitness)
  # Advance the top progeny according to the fitness function
  pop <- selectCross(pop=pop, trait=twoTraitFitFunc, nInd=nInd(pop)*selRat, nCrosses=nInd(pop))
}

save_dir <- file.path(output_dir, "BeneficialAlleles")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

fname <- file.path(save_dir, "beneficialalleles.pdf")
pdf(fname)

# Plot the ratio of segregating QTL to segregating alleles, along with fitness
par(mar = c(5, 5, 3, 5) + 0.3)
plot(df$gen, df$fitness, type="l", lwd = "3", col=2, xlab = "Generation", ylab = "Fitness")
par(new = TRUE) 
plot(df$gen, df$percFitInc, type="l", lwd = "3", col = 3, axes = FALSE, xlab = "", ylab = "") 
axis(side = 4, at = pretty(range(df$percFitInc)))
mtext("% of Segregating alleles that are QTL", side = 4, line = 3)
par(xpd=TRUE)
legend("right",
  c("Fitness", "% Alleles"),
       lty = 1,
       lwd = 3,
       col = 2:3)
dev.off()

This block runs n.nPops * n.sims Monte Carlo simulations of different populations to determine the change in the average effect size of alleles that are fixed along the adaptive walk, saving the order in which the alleles are fixed n.nPops populations are created, and each one undergoes n.sims unique adaptive walks

Right now, we do see an upward curve for the additive effect chart. A favored hypothesis: - Most of the alleles have a small effect size, so it is more likely that they would be fixed

n.sims <- 10
n.popResets <- 10
n.selProp <- 0.3
n.qtlPerChr <- 5
addSnpChip <- FALSE

fig <- plot_ly()

# Tidy dataframe to store the order in which each allele is fixed, and the effect size
eff_size_df <- data.frame(orderFixed=c(),
                          effectSizeA=c(),
                          effectSizeF=c())
# Create a new population n.nPops times
for (p in 1:n.popResets) {
  print(paste0("Pop Reset: ", p))
  source("Scripts/CreateFounderPop.R")
  for (s in 1:n.sims) {
    pop <- founderPop
    pop_df <- data.frame(gen=1:n.gens,
                         fitness=numeric(n.gens),
                         traitValA=numeric(n.gens),
                         traitValB=numeric(n.gens))
  
    print(paste0("Sim: ", s))
    # idx is the order in which an allele is fixed along an adaptive walk
    idx <- 1
    # whether or not to increment the idx counter. Multiple alleles may be fixed
    # in the same generation, so this cannot be incremented until each locus
    # has been examined
    inc <- FALSE
    
    # Burn-in
    for (gen in 1:n.burnInGens) {
      pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
    }
    
    # Main simulation
    for (gen in 1:n.gens) {
      meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
      selRat <- selectionRatio(meanFitness)
      pop_df$fitness[gen] <- meanFitness
      pop_df$traitValA[gen] <- meanP(pop)[1]
      pop_df$traitValB[gen] <- meanP(pop)[2]
      # Keep track of the current population
      prevPop <- pop
      # Advance the population based on fitness
      pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*selRat, nCrosses=nInd(pop))
      # Get the qtl genotypes from the current and new populations, so we can compare them
      prevGeno <- getUniqueQtl(prevPop)
      newGeno <- getUniqueQtl(pop)
      cols <- colnames(newGeno)
      n.loci <- length(cols)
      # Iterate through all loci
      for (l in 1:n.loci) {
        id <- cols[l]
        prevLocus = prevGeno[,l]
        newLocus = newGeno[,l]
        # Check if the allele was segregating and became fixed in this generation
        if (hetLocus(prevLocus) && !hetLocus(newLocus)) {
          # Increment the order counter after this generation
          inc <- TRUE
          # Determine the effect size, based on additivity
          effSizeA <- getEffectSize(prevLocus,
                                   id,
                                   prevPop,
                                   "Additive")
          #print(paste0("Gen: ", gen, ", Order: ", idx, ", Loc: ", id, ": ", effSizeA)) # REMOVE
          # Determine the effect size based on fitness
          effSizeF <- getEffectSize(prevLocus,
                                    id,
                                    prevPop,
                                    "Fitness")
          # Update the result dataframe
          new_row <- data.frame(orderFixed=c(idx),
                                effectSizeA=c(effSizeA),
                                effectSizeF=c(effSizeF))
          eff_size_df <- rbind(eff_size_df, new_row)
        }
      }
      
      # Check whether to increment the order counter and reset 'inc'
      if (inc) {
        idx <- idx + 1
        inc <- FALSE
      }
      # If the population is within n.margin, terminate the simulation
      if (mean(twoTraitFitFunc(gv(subPop))) >= n.margin) {
        break
      }
      
    }
    # Add the adaptive walk of the sub-population
    fig <- add_trace(
      fig,
      pop_df,
      name = s,
      x = pop_df$traitValA,
      y = pop_df$traitValB,
      z = pop_df$fitness,
      type = 'scatter3d',
      mode = 'lines',
      opacity = 1,
      color = s,
      line = list(width = 2)
    )
  }
}

save_dir <- file.path(output_dir, "AverageEffectSize")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

# Determine the average additive effect size at each 'step'
grouped_df_a <- eff_size_df %>%
  group_by(orderFixed) %>%
  summarize(meanEffectSize = mean(effectSizeA))

g_a <- ggplot(data=grouped_df_a, aes(x=orderFixed, y=meanEffectSize)) +
  geom_bar(stat="identity") +
  geom_smooth(formula = y ~ a^x)

ggplot2::ggsave(filename = "average_effect_size_additive.pdf",
                path=save_dir,
                device = "pdf")

# Determine the average fitness effect size at each 'step'
grouped_df_f <- eff_size_df %>%
  group_by(orderFixed) %>%
  summarize(meanEffectSize = mean(effectSizeF))

g_f <- ggplot(data=grouped_df_f, aes(x=orderFixed, y=meanEffectSize)) +
  geom_bar(stat="identity") +
  geom_smooth(formula = y ~ a^x)

ggplot2::ggsave(filename = "average_effect_size_fitness.pdf",
                path=save_dir,
                device = "pdf")

# Create a plot with the adaptive walks
p <- fig %>%
  layout(legend=list(title=list(text='Population')),
         showlegend=FALSE,
         scene = list(xaxis = list(title = "Trait A"),
                      yaxis = list(title = "Trait B"),
                      zaxis = list(title = "Fitness"),
                      aspectmode='cube')) %>% hide_colorbar()
fname <- file.path(save_dir, "adaptivewalk.html")
htmlwidgets::saveWidget(as_widget(p), fname)

This block will simulate one base population, from which two sub-populations are selected, and undergo purifying selection independently.

source("Scripts/GlobalVariables.R")

n.shape <- 1
n.var <- 0.05
n.qtlPerChr <- 2
n.segSites <- 1000
source("Scripts/CreateFounderPop.R")
n.subPopSize <- 500
n.selRat <- 0.5
n.r <- 0.9
n.gens <- 100
n.margin <- -0.05
plotTraitArchitecture(founderPop)
plot3dPopulationFitness(founderPop, calculateFitnessTwoTrait)
fit_df <- data.frame(gen=1:n.burnInGens,
                 fitness=numeric(n.burnInGens),
                 traitValA=numeric(n.burnInGens),
                 traitValB=numeric(n.burnInGens))
pop <- founderPop

# Burn-in generations
for (gen in 1:n.burnInGens) {
  fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
  fit_df$traitValA[gen] <- meanP(pop)[1]
  fit_df$traitValB[gen] <- meanP(pop)[2]
  pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}

# Create a random vector of size n.pops, with a random order of sub-population ids
randVec <- sample(rep(c(1:n.nPops), times=n.popSize/n.nPops))

# Select all of the "1" indexed individuals
popA <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=1, randVec=randVec)
# Select all of the "2" indexed individuals
popB <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=2, randVec=randVec)

# Create dataframes for each subpopulation, initializing with current values
popA_df <- data.frame(gen=c(1),
                 fitness=c(mean(twoTraitFitFunc(pheno(popA)))),
                 traitValA=c(meanP(popA)[1]),
                 traitValB=c(meanP(popA)[2]))
popB_df <- data.frame(gen=c(1),
                 fitness=c(mean(twoTraitFitFunc(pheno(popB)))),
                 traitValA=c(meanP(popB)[1]),
                 traitValB=c(meanP(popB)[2]))

# Iterate through the generations
for (gen in 1:n.gens) {
  # If popA is within the margin of the fitness optimum, don't progress it any further
  if (mean(twoTraitFitFunc(pheno(popA))) < n.margin) {
    # Advance the population
    meanFitness <- mean(twoTraitFitFunc(pheno(popA)))
    # Get a selection ratio based on fitness
    selRat <- selectionRatio(meanFitness)
    popA <- selectCross(popA, trait=twoTraitFitFunc, nInd=nInd(popA)*selRat, nCrosses=nInd(popA))
    # Update the dataframe with new values
    popA_df <- rbind(popA_df, data.frame(gen=gen,
                                       fitness=meanFitness,
                                       traitValA=meanP(popA)[1],
                                       traitValB=meanP(popA)[2]))
    
  }
  # If popB is within the margin of the fitness optimum, don't progress it any further
  if (mean(twoTraitFitFunc(pheno(popB))) < n.margin) {
    # If popA is within the margin of the fitness optimum, don't progress it any further
    meanFitness <- mean(twoTraitFitFunc(pheno(popB)))
    # Get a selection ratio based on fitnes
    selRat <- selectionRatio(meanFitness)
    popB <- selectCross(popB, trait=twoTraitFitFunc, nInd=nInd(popB)*selRat, nCrosses=nInd(popB))
    # Update the dataframe with new values
    popB_df <- rbind(popB_df, data.frame(gen=gen,
                                       fitness=meanFitness,
                                       traitValA=meanP(popB)[1],
                                       traitValB=meanP(popB)[2]))
  }
}
# Update rownames
rownames(popA_df) <- 1:nrow(popA_df)
rownames(popB_df) <- 1:nrow(popB_df)

# Plot the adaptive walks
fig <- plot_ly()
fig <- add_trace(
  fig,
  fit_df,
  name = "Burn In",
  x = fit_df$traitValA,
  y = fit_df$traitValB,
  z = fit_df$fitness,
  type = 'scatter3d',
  mode = 'lines',
  opacity = 1,
  color = 'yellow',
  line = list(width = 5)
)

fig <- add_trace(
    fig,
    popA_df,
    name = "Pop A",
    x = popA_df$traitValA,
    y = popA_df$traitValB,
    z = popA_df$fitness,
    type = 'scatter3d',
    mode = 'lines',
    opacity = 1,
    color = 'red',
    line = list(width = 5)
  )

fig <- add_trace(
    fig,
    popB_df,
    name = "Pop B",
    x = popB_df$traitValA,
    y = popB_df$traitValB,
    z = popB_df$fitness,
    type = 'scatter3d',
    mode = 'lines',
    opacity = 1,
    color = 'blue',
    line = list(width = 5)
  )


p <- fig %>%
  layout(legend=list(title=list(text='Population')),
         showlegend=FALSE,
         scene = list(xaxis = list(title = "Trait A"),
                      yaxis = list(title = "Trait B"),
                      zaxis = list(title = "Fitness"),
                      aspectmode='cube')) %>% hide_colorbar()

save_dir <- file.path(output_dir, "DivergingPopulations")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "adaptivewalks.html")
htmlwidgets::saveWidget(as_widget(p), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")

fname <- file.path(save_dir, "traitarchitecture.pdf")
pdf(fname)

p1 <- plotTraitArchitecture(popA, "Fitness", "popA")
p2 <- plotTraitArchitecture(popB, "Fitness", "popB")
p3 <- plotTraitArchitecture(popA, "Additive", "popA")
p4 <- plotTraitArchitecture(popB, "Additive", "popB")

(p1|p2)/(p3|p4)
dev.off()
quartz_off_screen 
                2 

# Create a density plot of trait 1
trait1.df <- as.data.frame(cbind(pheno(popA)[,1], pheno(popB)[,1]))
colnames(trait1.df) <- c("popA", "popB")
trait1.df <- trait1.df %>%
  pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t1 <- ggplot(trait1.df, aes(pheno, fill=pop, color=pop)) +
  geom_density(alpha=0.1) +
  labs(title="Trait 1")

# Create a density plot of trait 2
trait2.df <- as.data.frame(cbind(pheno(popA)[,2], pheno(popB)[,2]))
colnames(trait2.df) <- c("popA", "popB")
trait2.df <- trait2.df %>%
  pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t2 <- ggplot(trait2.df, aes(pheno, fill=pop, color=pop)) +
  geom_density(alpha=0.1) +
  labs(title="Trait 2")


(t1|t2)
ggplot2::ggsave(filename = "trait_distributions.pdf",
                path=save_dir,
                device = "pdf")
Saving 7.29 x 4.51 in image

plot3dPopulationFitnessTwoPops(popA, popB)
p
LS0tCnRpdGxlOiAiQWRhcHRpdmUgV2Fsa3MiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFRlZCBNb255YWsKZGVzY3JpcHRpb246IFRoaXMgbm90ZWJvb2sgY29udGFpbnMgc2NyaXB0cyBmb3IgdW5kZXJzdGFuZGluZyB0aGUgZHluYW1pY3Mgb2YgYWRhcHRpdmUgd2Fsa3MuCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0KcmVxdWlyZSgia25pdHIiKQpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIn4vRG9jdW1lbnRzL0NTVS9SL0JyZWVkaW5nU2ltcyIpCmBgYAoKSW1wb3J0cwpgYGB7cn0KbGlicmFyeShBbHBoYVNpbVIpCmxpYnJhcnkoZGV2dG9vbHMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoc25vdykKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodmlyaWRpcykKcm0obGlzdCA9IGxzKCkpCgpzZXQuc2VlZCgxMjMpCgpzb3VyY2UoIkZ1bmN0aW9ucy9GaXRuZXNzLlIiKQpzb3VyY2UoIkZ1bmN0aW9ucy9UcmFpdEFyY2hpdGVjdHVyZS5SIikKc291cmNlKCJTY3JpcHRzL0dsb2JhbFZhcmlhYmxlcy5SIikKc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCgpvdXRwdXRfZGlyIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiT3V0cHV0IikKaWYgKCFkaXIuZXhpc3RzKG91dHB1dF9kaXIpKSBkaXIuY3JlYXRlKG91dHB1dF9kaXIpCmBgYAoKVmlldyBhIGZpdG5lc3MgbGFuZHNjYXBlCmBgYHtyfQpwIDwtIHBsb3RGaXRuZXNzTGFuZHNjYXBlKCkKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiRml0bmVzc0Z1bmN0aW9ucyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJmaXRuZXNzX2Z1bmN0aW9uLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQpgYGAKClBsb3QgZGlmZmVyZW50IHRyYWl0IGFyY2hpdGVjdHVyZXMgYWNjb3JkaW5nIHRvIGRpZmZlcmVudCBhbGdvcml0aG1zCmBgYHtyfQpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlRyYWl0QXJjaGl0ZWN0dXJlIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCiMgQWRkaXRpdmUgZWZmZWN0cwptZXRob2RUeXBlIDwtICJBZGRpdGl2ZSIKZyA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUoZm91bmRlclBvcCwgbWV0aG9kVHlwZSkKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCJ0cmFpdGFyY2hpdGVjdHVyZV8iLCBtZXRob2RUeXBlLCAiLnBkZiIpLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBFZmZlY3Qgb2YgZWFjaCBhbGxlbGUgb24gZml0bmVzcwptZXRob2RUeXBlIDwtICJGaXRuZXNzIgpnIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShmb3VuZGVyUG9wLCBtZXRob2RUeXBlKQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoInRyYWl0YXJjaGl0ZWN0dXJlXyIsIG1ldGhvZFR5cGUsICIucGRmIiksCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgpwIDwtIHBsb3QzZFBvcHVsYXRpb25GaXRuZXNzKGZvdW5kZXJQb3AsIGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgIjNkcG9wdWxhdGlvbmZpdG5lc3MuaHRtbCIpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwKSwgZm5hbWUpCmBgYAoKU2ltdWxhdGUgc2V2ZXJhbCBhZGFwdGl2ZSB3YWxrcyB3aXRoIGRpZmZlcmVudCBwb3B1bGF0aW9uIHNpemVzCmBgYHtyfQojIERpZmZlcmVudCBzdGFydGluZyBwb3B1bGF0aW9uIHNpemVzCm4ucG9wU2l6ZXMgPC0gYyg0MCwyMDAsMTAwMCkKCiMgRG9uJ3QgdXNlIGEgU05QIGNoaXAgZm9yIHRoZXNlIHNpbXVsYXRpb25zCmFkZFNucENoaXAgPC0gRkFMU0UKCmZpZyA8LSBwbG90X2x5KCkKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zLAogICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5nZW5zKSkKCiMgSXRlcmF0ZSB0aHJvdWdoIGVhY2ggcG9wdWxhdGlvbiBzaXplCmZvciAocCBpbiBjKDE6bGVuZ3RoKG4ucG9wU2l6ZXMpKSkgewogIG4ucG9wU2l6ZSA8LSBuLnBvcFNpemVzW3BdCiAgcHJpbnQobi5wb3BTaXplKQogICMgQ3JlYXRlIGEgbmV3IHBvcHVsYXRpb24gb2YgdGhhdCBzaXplCiAgc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCiAgcG9wIDwtIGZvdW5kZXJQb3AKICAjIEl0ZXJhdGUgdGhyb3VnaCB0aGUgZ2VuZXJhdGlvbnMsIHVwZGF0ZSB0aGUgcmVzdWx0IGRhdGFmcmFtZSwgYW5kIGFkdmFuY2UKICAjIHByb2dlbnkgYmFzZWQgb24gdGhlIHR3byB0cmFpdCBmaXRuZXNzIGZ1bmNpb24KICBmb3IoZ2VuIGluIDE6bi5nZW5zKSB7CiAgICBmaXRfZGYkZml0bmVzc1tnZW5dIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKGd2KHBvcCkpKQogICAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5HKHBvcClbMV0KICAgIGZpdF9kZiR0cmFpdFZhbEJbZ2VuXSA8LSBtZWFuRyhwb3ApWzJdCiAgICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICAgIHNlbFJhdCA8LSBzZWxlY3Rpb25SYXRpbyhtZWFuRml0bmVzcykKICAgIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uLnBvcFNpemUqc2VsUmF0LCBuQ3Jvc3Nlcz1uLnBvcFNpemUpCiAgfQogICMgQWRkIGEgdHJhY2UgdG8gcmVwcmVzZW50IHRoaXMgcG9wdWxhdGlvbidzIGFkYXB0aXZlIHdhbGsKICBmaWcgPC0gYWRkX3RyYWNlKAogICAgZmlnLAogICAgZml0X2RmLAogICAgbmFtZSA9IG4ucG9wU2l6ZSwKICAgIHggPSBmaXRfZGYkdHJhaXRWYWxBLAogICAgeSA9IGZpdF9kZiR0cmFpdFZhbEIsCiAgICB6ID0gZml0X2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgY29sb3IgPSBwLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1KQogICkKICAKICAKfQoKZmlnIDwtIGZpZyAlPiUKICBsYXlvdXQobGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdQb3B1bGF0aW9uIFNpemUnKSksCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKCgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkRpZmZlcmVudFBvcHVsYXRpb25TaXplcyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIHBhc3RlMCgicG9wdWxhdGlvblNpemVzXyIsIG4ucG9wU2l6ZXNbMV0sICIsIiwgbi5wb3BTaXplc1syXSwgIiwiLCBuLnBvcFNpemVzWzNdLCAiLmh0bWwiKSkKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KGZpZyksIGZuYW1lKSAgCmBgYAoKT3ZlcmxheSBhbiBhZGFwdGl2ZSB3YWxrIG92ZXIgYSBmaXRuZXNzIGxhbmRzY2FwZSwgYW5kIHNhdmUgM0QgYW5kIDJEIHZlcnNpb25zCmBgYHtyfQpmaWcgPC0gcGxvdF9seSgpCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uZ2VucywKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW51bWVyaWMobi5nZW5zKSwKICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG4uZ2VucykpCgpwb3AgPC0gZm91bmRlclBvcAoKZm9yKGdlbiBpbiAxOm4uZ2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMoZ3YocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5HKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhbkcocG9wKVsyXQogIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogIHNlbFJhdCA8LSBzZWxlY3Rpb25SYXRpbyhtZWFuRml0bmVzcykKICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bi5wb3BTaXplKnNlbFJhdCwgbkNyb3NzZXM9bi5wb3BTaXplKQp9CgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIk92ZXJsYXlBZGFwdGl2ZVdhbGsiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKCnBsb3RUeXBlIDwtICJDT05UT1VSIgpwYyA8LSBvdmVybGF5V2Fsa09uTGFuZHNjYXBlKGZpdF9kZiwgdHlwZT1wbG90VHlwZSwgY2FsY3VsYXRlRml0bmVzc1R3b1RyYWl0KQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIHBhc3RlMCgiYWRhcHRpdmV3YWxrXyIsIHBsb3RUeXBlLCAiLmh0bWwiKSkKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjKSwgZm5hbWUpCgpwbG90VHlwZSA8LSAiU1VSRkFDRSIKcHMgPC0gb3ZlcmxheVdhbGtPbkxhbmRzY2FwZShmaXRfZGYsIHR5cGU9cGxvdFR5cGUsIGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBwYXN0ZTAoImFkYXB0aXZld2Fsa18iLCBwbG90VHlwZSwgIi5odG1sIikpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwcyksIGZuYW1lKQoKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQpgYGAKClBsb3QgdGhlIGNoYW5nZSBpbiBhbGxlbGUgZnJlcXVlbmN5IG92ZXIgdGltZQpgYGB7cn0Kbi5xdGxQZXJDaHIgPC0gMgpzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKcG9wIDwtIGZvdW5kZXJQb3AKCiMgUmVzdWx0cyBkYXRhZnJhbWUKZnJlcS5kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uZ2VucykKCiMgR2V0IHRoZSBlZmZlY3Qgc2l6ZXMgb2YgZWFjaCBxdGwKcXRsRWZmLmRmIDwtIGdldFF0bEVmZmVjdFNpemVzKHBvcCkKCiMgR2V0IHRoZSBuYW1lcyBvZiBhbGwgdGhlIFFUTHMKcXRsIDwtIHJvd25hbWVzKHF0bEVmZi5kZikKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIG9mIGFsbCB6ZXJvcyB3aGVyZSB0aGUgY29sdW1ucyBhcmUgdGhlIFFUTCBpZHMsIGFuZCB0aGUgIyByb3dzIGlzIHRoZSAjIG9mIGdlbmVyYXRpb25zCnF0bC5kZiA8LSBkYXRhLmZyYW1lKG1hdHJpeCgwLCBuY29sPWxlbmd0aChxdGwpLCBucm93PW4uZ2VucykpCmNvbG5hbWVzKHF0bC5kZikgPC0gcXRsCgojIENvbWJpbmUgdGhlIGRhdGFmcmFtZXMKZnJlcS5kZiA8LSBjYmluZChmcmVxLmRmLCBxdGwuZGYpCgojIEl0ZXJhdGUgdGhyb3VnaCBlYWNoIGdlbmVyYXRpb24KZm9yKGdlbiBpbiAxOm4uZ2VucykgewogIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogICMgR2V0IHRoZSBxdGwgZ2Vub3R5cGUgZGF0YQogIHF0bEdlbm8gPC0gZ2V0VW5pcXVlUXRsKHBvcCkKICAjIEdldCB0aGUgZnJlcXVlbmN5IG9mIHRoZSAnMicgYWxsZWxlIGF0IGVhY2ggbG9jdXMKICBmb3IgKGwgaW4gMTpsZW5ndGgocXRsKSkgewogICAgIyBpZCBpcyB0aGUgbmFtZSBvZiB0aGUgcXRsIChjaHJfc2l0ZSkKICAgIGlkIDwtIHF0bFtsXQogICAgIyBBIGxpc3Qgb2YgZ2Vub3R5cGUgZGF0YSBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBwb3B1bGF0aW9uIGF0IHRoYXQgbG9jdXMKICAgIGxvY3VzIDwtIHF0bEdlbm9bLGxdCiAgICAjIENhbGN1bGF0ZSB0aGUgYWxsZWxlIGZyZXF1ZW5jeSBhcyB0aGUgZnJlcXVlbmN5IG9mIGhvbW96eWdvdXMgaW5kaXZpZHVhbHMgKGZvciAnYWxsZWxlJykgKwogICAgIyAxLzIgKiBmcmVxdWVuY3kgb2YgaGV0ZXJvenlnb3VzIGluZGl2aWR1YWxzIChhc3N1bWVzIHRoZSBsb2N1cyBpcyBiaWFsbGVsaWMpCiAgICBmcmVxLmRmW2dlbixpZF0gPC0gKHN1bShsb2N1cz09bi5hbGxlbGUpL24ucG9wU2l6ZSkgKyAoKHN1bShsb2N1cz09MSkvbi5wb3BTaXplKS8yKQogIH0KICAjIERldGVybWluZSBzZWxlY3Rpb24gcmF0aW9uIGJhc2VkIG9uIGZpdG5lc3MKICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgI0FkdmFuY2UgaW5kaXZpZHVhbHMKICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bi5wb3BTaXplKnNlbFJhdCwgbkNyb3NzZXM9bi5wb3BTaXplKQp9CgojIE1ha2UgdGhlIGRhdGFmcmFtZSB0aWR5CmZyZXEuZGY8LSBtZWx0KGZyZXEuZGYsIGlkPSJnZW4iLCB2YXJpYWJsZS5uYW1lPSJRVEwgSUQiLCB2YWx1ZS5uYW1lPSJBbGxlbGUgRnJlcXVlbmN5IikKCiMgQWRkIHRoZSBxdGwgZWZmZWN0IHNpemUgZGF0YSB0byB0aGUgZGF0YWZyYW1lCmZyZXEuZGYgPC0gbWVyZ2UoZnJlcS5kZiwgcXRsRWZmLmRmLCBieS54PSJRVEwgSUQiLCBieS55PSJyb3cubmFtZXMiLCBhbGwueD1UUlVFKQoKIyBDcmVhdGUgYSBsaW5lIHBsb3QgZm9yIHRoZSBjaGFuZ2UgaW4gZnJlcXVlbmN5IG9mIGFsbGVsZXMgb3ZlciB0aW1lCiMgRWFjaCBsaW5lJ3Mgb3BhY2l0eSBpcyBhIGZ1bmN0aW9uIG9mIGl0cyBlZmZlY3Qgc2l6ZSAoaGlnaGVyPWRhcmtlcikKZyA8LSBnZ3Bsb3QoZnJlcS5kZiwgYWVzKHg9Z2VuLCB5PWZyZXEsIGNvbG9yPWlkLCBhbHBoYT1lZmZfc2l6ZSkpICsKICBnZW9tX2xpbmUoc2l6ZT0wLjcpCgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkFsbGVsZUZyZXF1ZW5jaWVzIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMCgiYWxsZWxlZnJlcXVlbmNpZXMucGRmIiksCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIsCiAgICAgICAgICAgICAgICB3aWR0aD0xMCwKICAgICAgICAgICAgICAgIGhlaWdodD03KQpgYGAKClNpbXVsYXRlIGFuIGFkYXB0aXZlIHdhbGssIGFuZCBwbG90IHRoZSBmb2xsb3dpbmcgY3VydmU6CiNzZWdyZWdhdGluZyBRVEwvI3NlZ3JlZ2F0aW5nIGxvY2kKCk9yaWdpbmFsIHF1ZXN0aW9uOiBvdXQgb2Ygc2VncmVnYXRpbmcgYWxsZWxlcyBsZWZ0IGluIHRoZSBwb3B1bGF0aW9uLCBob3cgbWFueQpvZiB0aGVtIHdvdWxkIGluY3JlYXNlIGZpdG5lc3M/IElzIHRoaXMgZXZlbiBhIHZhbGlkIHF1ZXN0aW9uPwoKVE9ETzoKLSBIb3cgZG9lcyB0aGlzIGNoYW5nZSB3aXRoIHNpemUgb2YgYWxsZWxlPwotIEhvdyBkb2VzIHNpemUgb2YgJ2ZpdG5lc3MgZGVsdGEnIGNoYW5nZSBvdmVyIGdlbmVyYXRpb25zPwotIFNpbXVhbGF0ZSBtdXRhdGlvbnMgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwgdG8gZmlndXJlIG91dCBQKGFsbGVsaWMgc3Vic3RpdHV0aW9uIGlzIGZhdm9yYWJsZSksCnNob3VsZCByZWZsZWN0IEZLTwotIFAobXV0YXRpb24gZ2V0cyBmaXhlZCkgLSBzaG93IHRoYXQgdGhpcyBtYXRjaGVzIEtpbXVyYQotIENvbXBhcmUgYWRhcHRpdmUgd2Fsa3MgZm9yIERIL2luYnJlZCBwb3B1bGF0aW9uIHdoZXJlIHRoZXJlIGFyZSBuZXcgbXV0YXRpb25zIFZTCnBvcHVsYXRpb24gdy9zdGFuZGluZyBnZW5ldGljIHZhcmlhdGlvbgpgYGB7cn0Kbi5nZW5zIDwtIDIwMAoKIyBSZXNldCB2YXJpYWJsZXMKc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCnNvdXJjZSgiU2NyaXB0cy9HbG9iYWxWYXJpYWJsZXMuUiIpCgpwb3AgPC0gZm91bmRlclBvcApkZiA8LSBkYXRhLmZyYW1lKGdlbj1jKCksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1jKCksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWwxPWMoKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9YygpLAogICAgICAgICAgICAgICAgIHBlcmNGaXRJbmM9YygpLAogICAgICAgICAgICAgICAgIHNlZ0FsbGVsZXM9YygpLAogICAgICAgICAgICAgICAgIHNlZ1F0bD1jKCkpCgojIEl0ZXJhdGUgdGhyb3VnaCB0aGUgZ2VuZXJhdGlvbnMKZm9yIChnZW4gaW4gMTpuLmdlbnMpIHsKICBwcmludChnZW4pCiAgIyBDb3VudGVyIHZhcmlhYmxlcwogIG51bUhldExvY2kgPSAwCiAgbnVtSGV0UXRsID0gMAogIG51bUluYyA9IDAKCiAgIyBHZXQgYWxsIG9mIHRoZSBsb2NpIGZyb20gdGhlIHBvcHVsYXRpb24KICBzZWdTaXRlR2VubyA8LSBwdWxsU2VnU2l0ZUdlbm8ocG9wKQogICMgR2V0IGFsbCBvZiB0aGUgUVRMIGZyb20gdGhlIHBvcHVsYXRpb24gKGEgc2FtcGxpbmcgb2YgdGhlIHNlZ1NpdGVHZW5vKQogIHF0bEdlbm8gPC0gZ2V0VW5pcXVlUXRsKHBvcCkKICBxdGxMb2NpIDwtIGNvbG5hbWVzKHF0bEdlbm8pCiAgbG9jaSA8LSBjb2xuYW1lcyhzZWdTaXRlR2VubykKICBuTG9jaSA8LSBuY29sKHNlZ1NpdGVHZW5vKQogICMgSXRlcmF0ZSB0aHJvdWdoIGFsbCBvZiB0aGUgbG9jaQogICMgVE9ETzoganVzdCBjYWxjdWxhdGUgbnVtSGV0UXRsIC8gbnVtSGV0TG9jaQogIGZvciAobCBpbiAxOm5jb2woc2VnU2l0ZUdlbm8pKSB7CiAgICBpZCA8LSBsb2NpW2xdCiAgICBsb2N1cyA9IHNlZ1NpdGVHZW5vWyxsXQogICAgIyBDaGVjayBpZiB0aGUgbG9jdXMgaXMgc2VncmVnYXRpbmcKICAgIGlmIChoZXRMb2N1cyhsb2N1cykpIHsKICAgICAgIyBJbmNyZW1lbnQgdGhlIGNvdW50ZXIKICAgICAgbnVtSGV0TG9jaSA8LSBudW1IZXRMb2NpICsgMQogICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUgLSB0aGlzIGlzIHdoZXJlIHRoZSBidWcgaXMKICAgICAgI2UgPC0gZ2V0RWZmZWN0U2l6ZShsb2N1cywgaWQsIHBvcCwgIkZpdG5lc3MiKQogICAgICAjaWYgKGUgPiAwKSB7CiAgICAgICMgIG51bUluYyA8LSBudW1JbmMgKzEKICAgICAgI30KICAgICAgIyBJZiB0aGUgbG9jdXMgaXMgYSBRVEwsIHRoZW4gaXQgaGFzIGFuIGVmZmVjdCBzaXplCiAgICAgIGlmIChpZCAlaW4lIHF0bExvY2kpIHsKICAgICAgICBudW1IZXRRdGwgPC0gbnVtSGV0UXRsICsgMQogICAgICB9CiAgICB9CiAgfQoKICAjIENhbGN1bGF0ZSB0aGUgcmF0aW8gb2Ygc2VncmVnYXRpbmcgUVRMIHRvIHNlZ3JlZ2F0aW5nIGFsbGVsZXMKICBpZiAobnVtSGV0TG9jaSA9PSAwKSB7CiAgICBwZXJjIDwtIDAKICB9IGVsc2UgewogICAgcGVyYyA8LSAobnVtSGV0UXRsIC8gbnVtSGV0TG9jaSkKICB9CiAgZGYgPC0gcmJpbmQoZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW1lYW4odHdvVHJhaXRGaXRGdW5jKGd2KHBvcCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDE9bWVhbkcocG9wKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9bWVhbkcocG9wKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjRml0SW5jPXBlcmMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnQWxsZWxlcz1udW1IZXRMb2NpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ1F0bD1udW1IZXRRdGwpKQogICMgSWYgdGhlIHBvcHVsYXRpb24gaXMgd2l0aGluIG4ubWFyZ2luLCB0ZXJtaW5hdGUgdGhlIHNpbXVsYXRpb24KICBpZiAobWVhbih0d29UcmFpdEZpdEZ1bmMoZ3YocG9wKSkpID49IG4ubWFyZ2luKSB7CiAgICBicmVhawogIH0KICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgIyBBZHZhbmNlIHRoZSB0b3AgcHJvZ2VueSBhY2NvcmRpbmcgdG8gdGhlIGZpdG5lc3MgZnVuY3Rpb24KICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wPXBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKfQoKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJCZW5lZmljaWFsQWxsZWxlcyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJiZW5lZmljaWFsYWxsZWxlcy5wZGYiKQpwZGYoZm5hbWUpCgojIFBsb3QgdGhlIHJhdGlvIG9mIHNlZ3JlZ2F0aW5nIFFUTCB0byBzZWdyZWdhdGluZyBhbGxlbGVzLCBhbG9uZyB3aXRoIGZpdG5lc3MKcGFyKG1hciA9IGMoNSwgNSwgMywgNSkgKyAwLjMpCnBsb3QoZGYkZ2VuLCBkZiRmaXRuZXNzLCB0eXBlPSJsIiwgbHdkID0gIjMiLCBjb2w9MiwgeGxhYiA9ICJHZW5lcmF0aW9uIiwgeWxhYiA9ICJGaXRuZXNzIikKcGFyKG5ldyA9IFRSVUUpIApwbG90KGRmJGdlbiwgZGYkcGVyY0ZpdEluYywgdHlwZT0ibCIsIGx3ZCA9ICIzIiwgY29sID0gMywgYXhlcyA9IEZBTFNFLCB4bGFiID0gIiIsIHlsYWIgPSAiIikgCmF4aXMoc2lkZSA9IDQsIGF0ID0gcHJldHR5KHJhbmdlKGRmJHBlcmNGaXRJbmMpKSkKbXRleHQoIiUgb2YgU2VncmVnYXRpbmcgYWxsZWxlcyB0aGF0IGFyZSBRVEwiLCBzaWRlID0gNCwgbGluZSA9IDMpCnBhcih4cGQ9VFJVRSkKbGVnZW5kKCJyaWdodCIsCiAgYygiRml0bmVzcyIsICIlIEFsbGVsZXMiKSwKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSAzLAogICAgICAgY29sID0gMjozKQpkZXYub2ZmKCkKYGBgCgpUaGlzIGJsb2NrIHJ1bnMgbi5uUG9wcyAqIG4uc2ltcyBNb250ZSBDYXJsbyBzaW11bGF0aW9ucyBvZiBkaWZmZXJlbnQgcG9wdWxhdGlvbnMgdG8gZGV0ZXJtaW5lCnRoZSBjaGFuZ2UgaW4gdGhlIGF2ZXJhZ2UgZWZmZWN0IHNpemUgb2YgYWxsZWxlcyB0aGF0IGFyZSBmaXhlZCBhbG9uZyB0aGUgYWRhcHRpdmUgd2FsaywKc2F2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGUgYWxsZWxlcyBhcmUgZml4ZWQKbi5uUG9wcyBwb3B1bGF0aW9ucyBhcmUgY3JlYXRlZCwgYW5kIGVhY2ggb25lIHVuZGVyZ29lcyBuLnNpbXMgdW5pcXVlIGFkYXB0aXZlIHdhbGtzCgpSaWdodCBub3csIHdlIGRvIHNlZSBhbiB1cHdhcmQgY3VydmUgZm9yIHRoZSBhZGRpdGl2ZSBlZmZlY3QgY2hhcnQuIEEgZmF2b3JlZCBoeXBvdGhlc2lzOgotIE1vc3Qgb2YgdGhlIGFsbGVsZXMgaGF2ZSBhIHNtYWxsIGVmZmVjdCBzaXplLCBzbyBpdCBpcyBtb3JlIGxpa2VseSB0aGF0IHRoZXkgd291bGQgYmUgZml4ZWQKYGBge3J9Cm4uc2ltcyA8LSAxMApuLnBvcFJlc2V0cyA8LSAxMApuLnNlbFByb3AgPC0gMC4zCm4ucXRsUGVyQ2hyIDwtIDUKYWRkU25wQ2hpcCA8LSBGQUxTRQoKZmlnIDwtIHBsb3RfbHkoKQoKIyBUaWR5IGRhdGFmcmFtZSB0byBzdG9yZSB0aGUgb3JkZXIgaW4gd2hpY2ggZWFjaCBhbGxlbGUgaXMgZml4ZWQsIGFuZCB0aGUgZWZmZWN0IHNpemUKZWZmX3NpemVfZGYgPC0gZGF0YS5mcmFtZShvcmRlckZpeGVkPWMoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RTaXplQT1jKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0U2l6ZUY9YygpKQojIENyZWF0ZSBhIG5ldyBwb3B1bGF0aW9uIG4ublBvcHMgdGltZXMKZm9yIChwIGluIDE6bi5wb3BSZXNldHMpIHsKICBwcmludChwYXN0ZTAoIlBvcCBSZXNldDogIiwgcCkpCiAgc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCiAgZm9yIChzIGluIDE6bi5zaW1zKSB7CiAgICBwb3AgPC0gZm91bmRlclBvcAogICAgcG9wX2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zLAogICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5nZW5zKSkKICAKICAgIHByaW50KHBhc3RlMCgiU2ltOiAiLCBzKSkKICAgICMgaWR4IGlzIHRoZSBvcmRlciBpbiB3aGljaCBhbiBhbGxlbGUgaXMgZml4ZWQgYWxvbmcgYW4gYWRhcHRpdmUgd2FsawogICAgaWR4IDwtIDEKICAgICMgd2hldGhlciBvciBub3QgdG8gaW5jcmVtZW50IHRoZSBpZHggY291bnRlci4gTXVsdGlwbGUgYWxsZWxlcyBtYXkgYmUgZml4ZWQKICAgICMgaW4gdGhlIHNhbWUgZ2VuZXJhdGlvbiwgc28gdGhpcyBjYW5ub3QgYmUgaW5jcmVtZW50ZWQgdW50aWwgZWFjaCBsb2N1cwogICAgIyBoYXMgYmVlbiBleGFtaW5lZAogICAgaW5jIDwtIEZBTFNFCiAgICAKICAgICMgQnVybi1pbgogICAgZm9yIChnZW4gaW4gMTpuLmJ1cm5JbkdlbnMpIHsKICAgICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpuLmJ1cm5JblNlbFByb3AsIG5Dcm9zc2VzPW5JbmQocG9wKSkKICAgIH0KICAgIAogICAgIyBNYWluIHNpbXVsYXRpb24KICAgIGZvciAoZ2VuIGluIDE6bi5nZW5zKSB7CiAgICAgIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogICAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICAgIHBvcF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbkZpdG5lc3MKICAgICAgcG9wX2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICAgICAgcG9wX2RmJHRyYWl0VmFsQltnZW5dIDwtIG1lYW5QKHBvcClbMl0KICAgICAgIyBLZWVwIHRyYWNrIG9mIHRoZSBjdXJyZW50IHBvcHVsYXRpb24KICAgICAgcHJldlBvcCA8LSBwb3AKICAgICAgIyBBZHZhbmNlIHRoZSBwb3B1bGF0aW9uIGJhc2VkIG9uIGZpdG5lc3MKICAgICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKICAgICAgIyBHZXQgdGhlIHF0bCBnZW5vdHlwZXMgZnJvbSB0aGUgY3VycmVudCBhbmQgbmV3IHBvcHVsYXRpb25zLCBzbyB3ZSBjYW4gY29tcGFyZSB0aGVtCiAgICAgIHByZXZHZW5vIDwtIGdldFVuaXF1ZVF0bChwcmV2UG9wKQogICAgICBuZXdHZW5vIDwtIGdldFVuaXF1ZVF0bChwb3ApCiAgICAgIGNvbHMgPC0gY29sbmFtZXMobmV3R2VubykKICAgICAgbi5sb2NpIDwtIGxlbmd0aChjb2xzKQogICAgICAjIEl0ZXJhdGUgdGhyb3VnaCBhbGwgbG9jaQogICAgICBmb3IgKGwgaW4gMTpuLmxvY2kpIHsKICAgICAgICBpZCA8LSBjb2xzW2xdCiAgICAgICAgcHJldkxvY3VzID0gcHJldkdlbm9bLGxdCiAgICAgICAgbmV3TG9jdXMgPSBuZXdHZW5vWyxsXQogICAgICAgICMgQ2hlY2sgaWYgdGhlIGFsbGVsZSB3YXMgc2VncmVnYXRpbmcgYW5kIGJlY2FtZSBmaXhlZCBpbiB0aGlzIGdlbmVyYXRpb24KICAgICAgICBpZiAoaGV0TG9jdXMocHJldkxvY3VzKSAmJiAhaGV0TG9jdXMobmV3TG9jdXMpKSB7CiAgICAgICAgICAjIEluY3JlbWVudCB0aGUgb3JkZXIgY291bnRlciBhZnRlciB0aGlzIGdlbmVyYXRpb24KICAgICAgICAgIGluYyA8LSBUUlVFCiAgICAgICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUsIGJhc2VkIG9uIGFkZGl0aXZpdHkKICAgICAgICAgIGVmZlNpemVBIDwtIGdldEVmZmVjdFNpemUocHJldkxvY3VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZQb3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFkZGl0aXZlIikKICAgICAgICAgICNwcmludChwYXN0ZTAoIkdlbjogIiwgZ2VuLCAiLCBPcmRlcjogIiwgaWR4LCAiLCBMb2M6ICIsIGlkLCAiOiAiLCBlZmZTaXplQSkpICMgUkVNT1ZFCiAgICAgICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUgYmFzZWQgb24gZml0bmVzcwogICAgICAgICAgZWZmU2l6ZUYgPC0gZ2V0RWZmZWN0U2l6ZShwcmV2TG9jdXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2UG9wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRml0bmVzcyIpCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcmVzdWx0IGRhdGFmcmFtZQogICAgICAgICAgbmV3X3JvdyA8LSBkYXRhLmZyYW1lKG9yZGVyRml4ZWQ9YyhpZHgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFNpemVBPWMoZWZmU2l6ZUEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFNpemVGPWMoZWZmU2l6ZUYpKQogICAgICAgICAgZWZmX3NpemVfZGYgPC0gcmJpbmQoZWZmX3NpemVfZGYsIG5ld19yb3cpCiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICAjIENoZWNrIHdoZXRoZXIgdG8gaW5jcmVtZW50IHRoZSBvcmRlciBjb3VudGVyIGFuZCByZXNldCAnaW5jJwogICAgICBpZiAoaW5jKSB7CiAgICAgICAgaWR4IDwtIGlkeCArIDEKICAgICAgICBpbmMgPC0gRkFMU0UKICAgICAgfQogICAgICAjIElmIHRoZSBwb3B1bGF0aW9uIGlzIHdpdGhpbiBuLm1hcmdpbiwgdGVybWluYXRlIHRoZSBzaW11bGF0aW9uCiAgICAgIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhndihzdWJQb3ApKSkgPj0gbi5tYXJnaW4pIHsKICAgICAgICBicmVhawogICAgICB9CiAgICAgIAogICAgfQogICAgIyBBZGQgdGhlIGFkYXB0aXZlIHdhbGsgb2YgdGhlIHN1Yi1wb3B1bGF0aW9uCiAgICBmaWcgPC0gYWRkX3RyYWNlKAogICAgICBmaWcsCiAgICAgIHBvcF9kZiwKICAgICAgbmFtZSA9IHMsCiAgICAgIHggPSBwb3BfZGYkdHJhaXRWYWxBLAogICAgICB5ID0gcG9wX2RmJHRyYWl0VmFsQiwKICAgICAgeiA9IHBvcF9kZiRmaXRuZXNzLAogICAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICAgIG1vZGUgPSAnbGluZXMnLAogICAgICBvcGFjaXR5ID0gMSwKICAgICAgY29sb3IgPSBzLAogICAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDIpCiAgICApCiAgfQp9CgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkF2ZXJhZ2VFZmZlY3RTaXplIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCiMgRGV0ZXJtaW5lIHRoZSBhdmVyYWdlIGFkZGl0aXZlIGVmZmVjdCBzaXplIGF0IGVhY2ggJ3N0ZXAnCmdyb3VwZWRfZGZfYSA8LSBlZmZfc2l6ZV9kZiAlPiUKICBncm91cF9ieShvcmRlckZpeGVkKSAlPiUKICBzdW1tYXJpemUobWVhbkVmZmVjdFNpemUgPSBtZWFuKGVmZmVjdFNpemVBKSkKCmdfYSA8LSBnZ3Bsb3QoZGF0YT1ncm91cGVkX2RmX2EsIGFlcyh4PW9yZGVyRml4ZWQsIHk9bWVhbkVmZmVjdFNpemUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgZ2VvbV9zbW9vdGgoZm9ybXVsYSA9IHkgfiBhXngpCgpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSAiYXZlcmFnZV9lZmZlY3Rfc2l6ZV9hZGRpdGl2ZS5wZGYiLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBEZXRlcm1pbmUgdGhlIGF2ZXJhZ2UgZml0bmVzcyBlZmZlY3Qgc2l6ZSBhdCBlYWNoICdzdGVwJwpncm91cGVkX2RmX2YgPC0gZWZmX3NpemVfZGYgJT4lCiAgZ3JvdXBfYnkob3JkZXJGaXhlZCkgJT4lCiAgc3VtbWFyaXplKG1lYW5FZmZlY3RTaXplID0gbWVhbihlZmZlY3RTaXplRikpCgpnX2YgPC0gZ2dwbG90KGRhdGE9Z3JvdXBlZF9kZl9mLCBhZXMoeD1vcmRlckZpeGVkLCB5PW1lYW5FZmZlY3RTaXplKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4gYV54KQoKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gImF2ZXJhZ2VfZWZmZWN0X3NpemVfZml0bmVzcy5wZGYiLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBDcmVhdGUgYSBwbG90IHdpdGggdGhlIGFkYXB0aXZlIHdhbGtzCnAgPC0gZmlnICU+JQogIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24nKSksCiAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UsCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiYWRhcHRpdmV3YWxrLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQpgYGAKClRoaXMgYmxvY2sgd2lsbCBzaW11bGF0ZSBvbmUgYmFzZSBwb3B1bGF0aW9uLCBmcm9tIHdoaWNoIHR3byBzdWItcG9wdWxhdGlvbnMgYXJlIHNlbGVjdGVkLCBhbmQgdW5kZXJnbyBwdXJpZnlpbmcgc2VsZWN0aW9uIGluZGVwZW5kZW50bHkuCmBgYHtyfQpzb3VyY2UoIlNjcmlwdHMvR2xvYmFsVmFyaWFibGVzLlIiKQpzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uYnVybkluR2VucywKICAgICAgICAgICAgICAgICBmaXRuZXNzPW51bWVyaWMobi5idXJuSW5HZW5zKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmJ1cm5JbkdlbnMpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG4uYnVybkluR2VucykpCnBvcCA8LSBmb3VuZGVyUG9wCgojIEJ1cm4taW4gZ2VuZXJhdGlvbnMKZm9yIChnZW4gaW4gMTpuLmJ1cm5JbkdlbnMpIHsKICBmaXRfZGYkZml0bmVzc1tnZW5dIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogIGZpdF9kZiR0cmFpdFZhbEFbZ2VuXSA8LSBtZWFuUChwb3ApWzFdCiAgZml0X2RmJHRyYWl0VmFsQltnZW5dIDwtIG1lYW5QKHBvcClbMl0KICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3ApKm4uYnVybkluU2VsUHJvcCwgbkNyb3NzZXM9bkluZChwb3ApKQp9CgojIENyZWF0ZSBhIHJhbmRvbSB2ZWN0b3Igb2Ygc2l6ZSBuLnBvcHMsIHdpdGggYSByYW5kb20gb3JkZXIgb2Ygc3ViLXBvcHVsYXRpb24gaWRzCnJhbmRWZWMgPC0gc2FtcGxlKHJlcChjKDE6bi5uUG9wcyksIHRpbWVzPW4ucG9wU2l6ZS9uLm5Qb3BzKSkKCiMgU2VsZWN0IGFsbCBvZiB0aGUgIjEiIGluZGV4ZWQgaW5kaXZpZHVhbHMKcG9wQSA8LSBzZWxlY3RJbmQocG9wLCB0cmFpdD1zZWxlY3RTdWJQb3AsIHNlbGVjdFRvcD1UUlVFLCBuSW5kPW4uc3ViUG9wU2l6ZSwgaWR4PTEsIHJhbmRWZWM9cmFuZFZlYykKIyBTZWxlY3QgYWxsIG9mIHRoZSAiMiIgaW5kZXhlZCBpbmRpdmlkdWFscwpwb3BCIDwtIHNlbGVjdEluZChwb3AsIHRyYWl0PXNlbGVjdFN1YlBvcCwgc2VsZWN0VG9wPVRSVUUsIG5JbmQ9bi5zdWJQb3BTaXplLCBpZHg9MiwgcmFuZFZlYz1yYW5kVmVjKQoKIyBDcmVhdGUgZGF0YWZyYW1lcyBmb3IgZWFjaCBzdWJwb3B1bGF0aW9uLCBpbml0aWFsaXppbmcgd2l0aCBjdXJyZW50IHZhbHVlcwpwb3BBX2RmIDwtIGRhdGEuZnJhbWUoZ2VuPWMoMSksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1jKG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEEpKSkpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1jKG1lYW5QKHBvcEEpWzFdKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9YyhtZWFuUChwb3BBKVsyXSkpCnBvcEJfZGYgPC0gZGF0YS5mcmFtZShnZW49YygxKSwKICAgICAgICAgICAgICAgICBmaXRuZXNzPWMobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPWMobWVhblAocG9wQilbMV0pLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1jKG1lYW5QKHBvcEIpWzJdKSkKCiMgSXRlcmF0ZSB0aHJvdWdoIHRoZSBnZW5lcmF0aW9ucwpmb3IgKGdlbiBpbiAxOm4uZ2VucykgewogICMgSWYgcG9wQSBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpIDwgbi5tYXJnaW4pIHsKICAgICMgQWR2YW5jZSB0aGUgcG9wdWxhdGlvbgogICAgbWVhbkZpdG5lc3MgPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQSkpKQogICAgIyBHZXQgYSBzZWxlY3Rpb24gcmF0aW8gYmFzZWQgb24gZml0bmVzcwogICAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogICAgcG9wQSA8LSBzZWxlY3RDcm9zcyhwb3BBLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3BBKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wQSkpCiAgICAjIFVwZGF0ZSB0aGUgZGF0YWZyYW1lIHdpdGggbmV3IHZhbHVlcwogICAgcG9wQV9kZiA8LSByYmluZChwb3BBX2RmLCBkYXRhLmZyYW1lKGdlbj1nZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bWVhbkZpdG5lc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1tZWFuUChwb3BBKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW1lYW5QKHBvcEEpWzJdKSkKICAgIAogIH0KICAjIElmIHBvcEIgaXMgd2l0aGluIHRoZSBtYXJnaW4gb2YgdGhlIGZpdG5lc3Mgb3B0aW11bSwgZG9uJ3QgcHJvZ3Jlc3MgaXQgYW55IGZ1cnRoZXIKICBpZiAobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKSA8IG4ubWFyZ2luKSB7CiAgICAjIElmIHBvcEEgaXMgd2l0aGluIHRoZSBtYXJnaW4gb2YgdGhlIGZpdG5lc3Mgb3B0aW11bSwgZG9uJ3QgcHJvZ3Jlc3MgaXQgYW55IGZ1cnRoZXIKICAgIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEIpKSkKICAgICMgR2V0IGEgc2VsZWN0aW9uIHJhdGlvIGJhc2VkIG9uIGZpdG5lcwogICAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogICAgcG9wQiA8LSBzZWxlY3RDcm9zcyhwb3BCLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3BCKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wQikpCiAgICAjIFVwZGF0ZSB0aGUgZGF0YWZyYW1lIHdpdGggbmV3IHZhbHVlcwogICAgcG9wQl9kZiA8LSByYmluZChwb3BCX2RmLCBkYXRhLmZyYW1lKGdlbj1nZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bWVhbkZpdG5lc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1tZWFuUChwb3BCKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW1lYW5QKHBvcEIpWzJdKSkKICB9Cn0KIyBVcGRhdGUgcm93bmFtZXMKcm93bmFtZXMocG9wQV9kZikgPC0gMTpucm93KHBvcEFfZGYpCnJvd25hbWVzKHBvcEJfZGYpIDwtIDE6bnJvdyhwb3BCX2RmKQoKIyBQbG90IHRoZSBhZGFwdGl2ZSB3YWxrcwpmaWcgPC0gcGxvdF9seSgpCmZpZyA8LSBhZGRfdHJhY2UoCiAgZmlnLAogIGZpdF9kZiwKICBuYW1lID0gIkJ1cm4gSW4iLAogIHggPSBmaXRfZGYkdHJhaXRWYWxBLAogIHkgPSBmaXRfZGYkdHJhaXRWYWxCLAogIHogPSBmaXRfZGYkZml0bmVzcywKICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgbW9kZSA9ICdsaW5lcycsCiAgb3BhY2l0eSA9IDEsCiAgY29sb3IgPSAneWVsbG93JywKICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCikKCmZpZyA8LSBhZGRfdHJhY2UoCiAgICBmaWcsCiAgICBwb3BBX2RmLAogICAgbmFtZSA9ICJQb3AgQSIsCiAgICB4ID0gcG9wQV9kZiR0cmFpdFZhbEEsCiAgICB5ID0gcG9wQV9kZiR0cmFpdFZhbEIsCiAgICB6ID0gcG9wQV9kZiRmaXRuZXNzLAogICAgdHlwZSA9ICdzY2F0dGVyM2QnLAogICAgbW9kZSA9ICdsaW5lcycsCiAgICBvcGFjaXR5ID0gMSwKICAgIGNvbG9yID0gJ3JlZCcsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCiAgKQoKZmlnIDwtIGFkZF90cmFjZSgKICAgIGZpZywKICAgIHBvcEJfZGYsCiAgICBuYW1lID0gIlBvcCBCIiwKICAgIHggPSBwb3BCX2RmJHRyYWl0VmFsQSwKICAgIHkgPSBwb3BCX2RmJHRyYWl0VmFsQiwKICAgIHogPSBwb3BCX2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgY29sb3IgPSAnYmx1ZScsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCiAgKQoKCnAgPC0gZmlnICU+JQogIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24nKSksCiAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UsCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiRGl2ZXJnaW5nUG9wdWxhdGlvbnMiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiYWRhcHRpdmV3YWxrcy5odG1sIikKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHApLCBmbmFtZSkKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgIjJQb3B1bGF0aW9uRml0bmVzcy5odG1sIikKcCA8LSBwbG90M2RQb3B1bGF0aW9uRml0bmVzc1R3b1BvcHMocG9wQSwgcG9wQikKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHApLCBmbmFtZSkKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQoKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAidHJhaXRhcmNoaXRlY3R1cmUucGRmIikKcGRmKGZuYW1lKQoKcDEgPC0gcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKHBvcEEsICJGaXRuZXNzIiwgInBvcEEiKQpwMiA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQiwgIkZpdG5lc3MiLCAicG9wQiIpCnAzIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BBLCAiQWRkaXRpdmUiLCAicG9wQSIpCnA0IDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BCLCAiQWRkaXRpdmUiLCAicG9wQiIpCgoocDF8cDIpLyhwM3xwNCkKZGV2Lm9mZigpCgojIENyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0cmFpdCAxCnRyYWl0MS5kZiA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHBoZW5vKHBvcEEpWywxXSwgcGhlbm8ocG9wQilbLDFdKSkKY29sbmFtZXModHJhaXQxLmRmKSA8LSBjKCJwb3BBIiwgInBvcEIiKQp0cmFpdDEuZGYgPC0gdHJhaXQxLmRmICU+JQogIHBpdm90X2xvbmdlcihjKCJwb3BBIiwgInBvcEIiKSwgbmFtZXNfdG89InBvcCIsIHZhbHVlc190bz0icGhlbm8iKQp0MSA8LSBnZ3Bsb3QodHJhaXQxLmRmLCBhZXMocGhlbm8sIGZpbGw9cG9wLCBjb2xvcj1wb3ApKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuMSkgKwogIGxhYnModGl0bGU9IlRyYWl0IDEiKQoKIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3Qgb2YgdHJhaXQgMgp0cmFpdDIuZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChwaGVubyhwb3BBKVssMl0sIHBoZW5vKHBvcEIpWywyXSkpCmNvbG5hbWVzKHRyYWl0Mi5kZikgPC0gYygicG9wQSIsICJwb3BCIikKdHJhaXQyLmRmIDwtIHRyYWl0Mi5kZiAlPiUKICBwaXZvdF9sb25nZXIoYygicG9wQSIsICJwb3BCIiksIG5hbWVzX3RvPSJwb3AiLCB2YWx1ZXNfdG89InBoZW5vIikKdDIgPC0gZ2dwbG90KHRyYWl0Mi5kZiwgYWVzKHBoZW5vLCBmaWxsPXBvcCwgY29sb3I9cG9wKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjEpICsKICBsYWJzKHRpdGxlPSJUcmFpdCAyIikKCgoodDF8dDIpCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9ICJ0cmFpdF9kaXN0cmlidXRpb25zLnBkZiIsCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgoKYGBgCg==